// kernel/pmm.c

#include "types.h"
#include "defs.h"
#include "param.h"
#include "spinlock.h"
#include "memlayout.h"
#include "pmm.h"

extern char end[]; // 内核结束地址，来自链接脚本

// 物理内存管理器全局状态
struct pmm_info pmm;

// 初始化物理内存管理器
void pmm_init(void)
{ 
    initlock(&pmm.lock, "pmm");
    
    // 计算可用物理内存范围
    uint64 start = PGROUNDUP((uint64)end);
    uint64 end_mem = PHYSTOP;
    
    pmm.total_pages = (end_mem - start) / PAGE_SIZE;
    pmm.free_pages = 0;
    
    // 初始化位图
    uint64 bitmap_size = (pmm.total_pages + 7) / 8;
    pmm.page_allocated = (uint8*)start;
    start += PGROUNDUP(bitmap_size);
    // 构建空闲链表
    pmm.freelist = 0;
    freerange((void*)start, (void*)end_mem);
    
    cprintf("pmm: initialized, %d pages available\n", pmm.free_pages);
}

// 释放内存范围
void freerange(void *vstart, void *vend)
{
    char *p = (char*)PGROUNDUP((uint64)vstart);
    //printf("freerange: %p to %p, first page: %p\n", vstart, vend, p);
    for(; p + PAGE_SIZE <= (char*)vend; p += PAGE_SIZE) {
        free_page(p);
    }
}

// 分配单页
void* alloc_page(void)
{
    acquire(&pmm.lock);
    
    if(pmm.freelist == 0) {
        release(&pmm.lock);
        panic("alloc_page: out of memory");
    }
    
    struct run *r = pmm.freelist;
    pmm.freelist = r->next;
    pmm.free_pages--;
    
    // 标记页面为已分配
    uint64 page_index = ((uint64)r - KERNBASE) / PAGE_SIZE;
    pmm.page_allocated[page_index / 8] |= (1 << (page_index % 8));
    
    // 清空页面内容
    memset(r, 0, PAGE_SIZE);
    
    release(&pmm.lock);
    return (void*)r;
}

// 释放页面
void free_page(void *page)
{
    if((uint64)page % PAGE_SIZE != 0 || (uint64)page < KERNBASE || 
       (uint64)page >= PHYSTOP) {
        panic("free_page: invalid page");
    }
    
    acquire(&pmm.lock);
    
    // 标记页面为未分配
    uint64 page_index = ((uint64)page - KERNBASE) / PAGE_SIZE;
    pmm.page_allocated[page_index / 8] &= ~(1 << (page_index % 8));
    
    struct run *r = (struct run*)page;
    r->next = pmm.freelist;
    pmm.freelist = r;
    pmm.free_pages++;
    
    release(&pmm.lock);
}

// 分配连续页面
void* alloc_pages(int n)
{
    if(n <= 0) return 0;
    
    acquire(&pmm.lock);
    
    // 简单实现：多次分配单页（实际应该使用更高效的算法）
    void *result = 0;
    void **pages = (void**)alloc_page(); // 临时存储分配的页面
    
    if(pages) {
        int i;
        for(i = 0; i < n; i++) {
            pages[i] = alloc_page();
            if(!pages[i]) break;
        }
        
        if(i == n) {
            result = pages[0];
            // 确保连续性（这里简化处理）
        } else {
            // 分配失败，释放已分配的页面
            for(int j = 0; j < i; j++) {
                free_page(pages[j]);
            }
            free_page(pages);
        }
    }
    
    release(&pmm.lock);
    return result;
}

// 内存统计
void pmm_stats(void)
{
    acquire(&pmm.lock);
    cprintf("pmm: total pages: %d, free pages: %d\n", 
            pmm.total_pages, pmm.free_pages);
    release(&pmm.lock);
}